Skip to content
On this page

基础篇 2-后端技术选型 —— Node.js & hapi


基础篇 2 :后端技术选型 —— Node.js & hapi

前一节我们明确了系统设计目标:一个用以支撑小程序外卖业务的后端系统。那么针对该目标,选择什么样的技术路线,则是一个重要的设计环节。这好比为一场旅行所做的攻略,选择 A 路线还是 B 路线,一旦选择了,后面所走的弯路哭着也要走完。这是一个特别考验经验深度与视野广度的设计阶段。

Node.js

小程序的全栈之路,Node.js 后端服务应运而生

我们在开篇章节中,从天然的前后端分离、一致化的 JavaScript 语言、微服务的轻重快慢的维度,已经对小程序后端服务的技术选型作了一些基础的介绍。考虑本小册读者很多是小程序的前端开发者,笔者抱着以同一语言全栈开发的初衷,来进行后端服务框架选型,因此优先选择 Node.js。但选择 Node.js 的原因仅出自于情怀么?当然不是。

Node.js 诞生于 2009 年,在随后的几年,凭借其高性能、易部署等特点,迅速在前端领域脱颖而出。Node.js 本为解决后端并发而生,却无心插柳,成了大前端的基石。伴随着应用场景从脚手架、辅助前端开发、到中间层、代理层,到专业的后端开发,都有相当的工程实践案例积累。

Node.js 的大型项目应用背书

PayPal

PayPal 需要为全世界 2 亿活跃用户提供服务,而它做得非常完美。刚开始,PayPal 需要将团队成员分工,分别开发前后端应用。自从 PayPal 选择使用 Node.js 替代 Java 开发后端,整个团队只需要使用一种编程语言 — JavaScript。 根据 Node.js at PayPal,使用 Node.js 之后,应用开发速度提高了 2 倍,代码量减少了 33%,文件数目减少了 40%,并且,每秒处理的请求数增加了 2 倍,接口的请求时间减少了 35%。

LinkedIn

LinkedIn 拥有 4.5 亿用户,2016 年微软以 260 亿美元收购了它。LinkedIn 的移动应用的后端是由 Ruby on Rails 切换到了 Node.js。根据 LinkedIn Moved From Rails To Node.js,优异的性能和扩展性是 LinkedIn 选择 Node.js 的主要原因。使用 Node.js 之后:某些场景下,性能提高 20 倍, 服务器由 30 个减少到了 3 个。

eBay

eBay 工程师们在对于服务的技术选型中,也陷入过不少的取舍纠结,最终选择了 Node.js,因为他们对实时性要求非常高。根据 How We Built eBay’s First Node.js Application 的文章所述,eBay 尝试用 Node.js 开发一个应用之后,就将整个后端从 Java 都迁移到了 Node.js 。eBay 有 1.7 亿活跃用户,这说明 Node.js 具备处理大量的网络请求的能力。

……

Node.js 的经典框架

Express

Express 是一个简洁而灵活的 Node.js Web 应用框架, 提供一系列强大特性帮助你创建各种 Web 应用和丰富的 HTTP 工具。

Express 对 Node.js 已有的特性不进行二次抽象,我们在日常使用中,只是在它的基础上扩展了 Web 应用所需的功能。丰富的 HTTP 工具与中间件按需组合,创建强健、友好的 API 变得快速而简单, 使用 Express 可以快速地完成一个完整功能的网站搭建。

Express 框架核心特征:

  • 可以设置中间件来响应 HTTP 请求
  • 定义了路由表用于执行不同的 HTTP 请求动作
  • 可以通过向模板传递参数来动态渲染 HTML 页面

Koa

由 Express 原班人马打造的 Koa,致力于成为一个更小、更富有表现力、更健壮的 Web 开发框架。

使用 Koa 编写 Web 应用,通过组合不同的 async/await,可以免除重复繁琐的回调函数嵌套, 并极大地提升常用错误处理效率。Koa 不在内核中打包任何中间件,仅集成了一套优雅的函数库,来使 Web 应用的编写高效便捷。

Koa 框架核心特征:

  • 洋葱圈模型的中间件设计思想,可以很好地被前后端分离的思路利用起来,比如:路由、模板引擎、数据代理等等
  • 提供更强语意化的请求的上下文 context 对象
  • 基于 ES6 的 async/await 特性解决异步问题

再看 Node.js —— hapi

NpmTrends 下载趋势图

背景

hapi 是 Web 应用程序的开源框架。最常见使用 hapi 的是构建 Web 服务,如 JSON API。你可以使用 hapi 构建 API 服务器,网站和 HTTP 代理应用程序。

hapi 是由 Eran Hammer 领导的沃尔玛实验室移动团队创建的,以处理像黑色星期五这样的活动,这是美国日历上最繁忙的在线购物日之一。Eran Hammer 还因创建 OAuth 闻名。

hapi 的原始版本使用 Express 框架。沃尔玛发现 Express 的限制使得该框架不适合他们的特殊要求。Express 缺少一些主要功能,所以沃尔玛最终将 hapi 发展到自己的独立框架。

hapi 是一个用来构建基于 Node.js 的应用和服务的富框架,能够帮助我们把关注重点放在高可用的应用业务逻辑层而不是构建架构。hapi 的官方 GitHub Repositories 中,提供了大量的常用高质量插件(100% 测试覆盖率),基本覆盖 Web 应用开发常用的功能,比如输入验证、缓存、认证等。

  • Lab & Code 测试插件
  • Joi 面向 Object Schema 的验证器插件
  • Bell 第三方登录插件
  • Good 监控日志相关插件
  • Boom 友好的 HTTP 错误返回插件
  • h2o2 代理转发插件
  • Catbox 缓存策略插件
  • hapi-auth-cookie 基于 Cookie 的用户认证插件
  • Inert 静态文件资源管理插件
  • tv 可交互式的 debug 控制台
  • Vision 网页模板渲染插件
  • and so on ...

hapi 也是建立代理的好选择。例如,沃尔玛正在使用 hapi 将其 API 的请求转发到外部 Java 服务。如果要构建单页应用程序,并且拥有多个后端服务器,即使它们与 Web 应用程序在同一主机上,但在不同端口上,也会遇到跨源资源共享问题。你可以用代理的帮助来解决这些问题。

hapi 与社交应用程序和实时聊天应用程序运作良好。我们不需要为实时应用程序创建自定义插件,因为 hapi 提供了一个名为 nes 的插件用于实时聊天应用程序,但对于这本小册,这个特性不是我们的关注重点。

hapi 的设计哲学

  • 推崇「配置优于代码」的原则,可以让开发者一定程度从繁杂的业务逻辑中解脱
  • 官方提供了许多高质量的插件(单测覆盖率达 100%),基本涵盖 Web 开发的方方面面
  • 是市面上和 Swagger 结合最好的 Node server ,没有之一

hapi 与 Express 的差别对比

设计哲学对比

Express 的设计初衷,在于其自身的极简特性。通过在 HTTP 顶层为开发者提供极少量的 API,开发者们可以在添加其他功能方面保持独立和自己的偏好,但也变相提升开发者对第三方插件的辨别与学习成本。

hapi 具有丰富的官方功能集,带来相对一致化的开发体验。并通常通过配置 options 来暴露,而不需要编写逻辑代码。例如,如果我们想确保在运行控制器 (controller)具体逻辑之前将请求主体 (request body) 完全读入内存并进行适当的解析(自动基于 Content-Type),只需要如下简单配置即可:

server.route({
    config: {
        payload: {
            output: 'data',
            parse: true
        }
    },
    method: 'GET',
    path: '/',
    handler: function (request, reply) {
        reply(request.payload);
    }
});

官方 API 文档对比

对比两者的官方 API 文档,hapi 相比 Express 提供一套更强大的功能集,包含了如下 Express 不具备的功能:

  • 入参与出参的验证 (通过 Joi )
  • 仅需少量代码,即可针对多种存储方式,完成服务端缓存配置( Mongo, S3, Redis, Riak)
  • cookie-parsing
  • sessions
  • 文件上传与 multipart 解析
  • CORS 支持
  • 日志系统

可扩展性与模块化对比

hapi 和 Express 以完全不同的方式实现可扩展性。用 Express,你可以使用中间件功能。中间件函数有点像过滤器,你可以在命中 controller 逻辑层之前堆叠所有请求。

hapi 能够请求生命周期并提供扩展点,这些扩展点可与中间件功能相媲美,但在请求生命周期中存在多个已定义的点。

沃尔玛构建 hapi 并停止使用 Express 的原因之一是:他们将 Express 应用程序拆分成单独的子系统的时候,希望让不同的团队成员安全地在各自的模块上工作,却遇到了相当大的困难。出于这个原因,他们用 hapi 创建了插件系统。

插件就像一个子应用程序,我们可以在 hapi 应用程序中执行所有操作,添加路由,扩展点等。在插件中,我们可以确定你没有破坏应用程序的其他部分,因为路由注册的顺序无关紧要,我们无法创建冲突路由。然后,我们可以将此插件组合到服务器中并进行部署。

生态系统对比

因为 Express 可以让我们开箱即用,所以当我们需要向项目中添加任何内容时,我们需要向外寻求解决方案。在使用 hapi 时,很多时候,我们需要的功能是内置的,或者是核心团队创建的模块。

小而美的系统很棒。但如果我们正在构建一个严肃的系统程序,那么我们最终可能依旧需要那些基础框架之外的所有轮子。

安全性对比

hapi 由沃尔玛的团队设计,用于运行黑色星期五的大流量访问,因此安全性和稳定性始终是最受关注的问题。出于这个原因,框架中加入了许多额外的容错机制,例如限制传入的有效 payload 大小以防止耗尽进程内存。它还具有最大事件循环延迟,使用最大 RSS 内存和使用 v8 heap 的最大值等选项,超过这些选项,服务器将以 503 超时响应,而不是仅仅崩溃。

关于 hapi v16 与 v17 的补充说明

本小册的案例,建立在 hapi v16 的版本,v17 是 hapi 的一次对底层实现破坏性的大版本升级,引起的问题便是周边的生态插件对最新版本 v17 的良好适配性存在一定的滞后,导致开发体验下降。故而我们以相对更稳定的 v16 作为案例切入,随着后续的生态完善化,我们可以逐渐将系统升级到 v17。

小结

关键词:Node.js,Express,Koa,hapi

技术的选型是一个整体化思维的过程,没有绝对的银弹。本小节希望以 Node.js 为例,从技术自身的理论特点、商业案例的背书、技术特点的对比等多个纬度进行理性考量,最终有理有据地作出取舍。每个人的知识储备和技术视角各有不同,但相信持续实践有方法论的理性技术选型,架构功力一定能有所成长与提高。

思考:全家桶式的解决方案比如 hapi ,与自助 DIY 式的解决方案比如 Express,在做技术选型时并不存在孰优孰劣,但对于做选型决策的你,是否对所做决策过程中的取舍了然于心呢?取之有道,那就自信而果敢地开始实践吧。